﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Noris.Tools.FrxEditor.Components
{
    #region struct PaddingF
    /// <summary>
    /// Struktura Padding ve float hodnotách
    /// </summary>
    public struct PaddingF : IXmlString
    {
        /// <summary>
        /// Konstruktor z XML stringu
        /// </summary>
        /// <param name="xmlString"></param>
        public PaddingF(string xmlString)
        {
            float all = 0f;
            this._Top = all;
            this._Left = all;
            this._Right = all;
            this._Bottom = all;
            this._XmlStringFill(xmlString);
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="top"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="bottom"></param>
        public PaddingF(float all)
        {
            this._Top = all;
            this._Left = all;
            this._Right = all;
            this._Bottom = all;
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="top"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="bottom"></param>
        public PaddingF(float top, float left, float right, float bottom)
        {
            this._Top = top;
            this._Left = left;
            this._Right = right;
            this._Bottom = bottom;
        }
        private float _Top;
        private float _Left;
        private float _Right;
        private float _Bottom;
        public override string ToString()
        {
            return
                "Top=" + this.Top.ToString() + "; " +
                "Left=" + this.Left.ToString() + "; " +
                "Right=" + this.Right.ToString() + "; " +
                "Bottom=" + this.Bottom.ToString();
        }
        /// <summary>
        /// Lze vložit všechny souřadnice najednou.
        /// Pokud jsou všechny souřadnice shodné, pak zde je jejich hodnota.
        /// Pokud je některá souřadnice odlišná od ostatních, pak je zde null.
        /// </summary>
        public float? All
        {
            get
            {
                float? result = this.Top;
                if (result.HasValue && result.Value != this.Left) result = null;
                if (result.HasValue && result.Value != this.Right) result = null;
                if (result.HasValue && result.Value != this.Bottom) result = null;
                return result;
            }
            set
            {
                if (value.HasValue)
                {
                    float all = value.Value;
                    this._Top = all;
                    this._Left = all;
                    this._Right = all;
                    this._Bottom = all;
                }
            }
        }
        public float Top { get { return this._Top; } set { this._Top = value; } }
        public float Left { get { return this._Left; } set { this._Left = value; } }
        public float Right { get { return this._Right; } set { this._Right = value; } }
        public float Bottom { get { return this._Bottom; } set { this._Bottom = value; } }
        public float Vertical { get { return this._Top + this._Bottom; } }
        public float Horizonal { get { return this._Left + this._Right; } }

        public static bool operator ==(PaddingF p1, PaddingF p2)
        {
            return (p1._Left == p2._Left && p1._Top == p2._Top && p1._Right == p2._Right && p1._Bottom == p2._Bottom);
        }
        public static bool operator !=(PaddingF p1, PaddingF p2)
        {
            return (!(p1 == p2));
        }
        public static PaddingF operator +(PaddingF p1, PaddingF p2)
        {
            return new PaddingF(p1._Left + p2._Left, p1._Top + p2._Top, p1._Right + p2._Right, p1._Bottom + p2._Bottom);
        }
        public static PaddingF operator -(PaddingF p1, PaddingF p2)
        {
            return new PaddingF(p1._Left - p2._Left, p1._Top - p2._Top, p1._Right - p2._Right, p1._Bottom - p2._Bottom);
        }
        /// <summary>
        /// Vrátí prostor uvnitř prostoru dané velikosti.
        /// Souřadnice se vztahují k objektu, jehož velikost je předaná.
        /// </summary>
        /// <param name="outerSize"></param>
        /// <returns></returns>
        public RectangleF GetInnerArea(SizeF outerSize)
        {
            return _GetArea(new PointF(0f, 0f), outerSize, 1f);
        }
        /// <summary>
        /// Vrátí prostor uvnitř prostoru dané velikosti.
        /// Souřadnice se vztahují k objektu, jehož velikost je předaná.
        /// </summary>
        /// <param name="outerSize"></param>
        /// <returns></returns>
        public RectangleF GetInnerArea(RectangleF outerArea)
        {
            return _GetArea(outerArea.Location, outerArea.Size, 1f);
        }
        /// <summary>
        /// Vrátí prostor vně prostoru dané velikosti.
        /// Souřadnice se vztahují k objektu, jehož velikost je předaná.
        /// </summary>
        /// <param name="outerSize"></param>
        /// <returns></returns>
        public RectangleF GetOuterArea(SizeF outerSize)
        {
            return _GetArea(new PointF(0f, 0f), outerSize, -1f);
        }
        /// <summary>
        /// Vrátí prostor vně prostoru dané velikosti.
        /// Souřadnice se vztahují k objektu, jehož velikost je předaná.
        /// </summary>
        /// <param name="outerSize"></param>
        /// <returns></returns>
        public RectangleF GetOuterArea(RectangleF outerArea)
        {
            return _GetArea(outerArea.Location, outerArea.Size, -1f);
        }
        private RectangleF _GetArea(PointF basePoint, SizeF baseSize, float coefficient)
        {
            return new RectangleF(
                basePoint.X + coefficient * this.Left,
                basePoint.Y + coefficient * this.Top,
                baseSize.Width - coefficient * this.Horizonal,
                baseSize.Height - coefficient * this.Vertical);
        }
        /// <summary>
        /// naplnění povinností interface IXmlString
        /// </summary>
        string IXmlString.XmlString
        {
            get { return this._XmlStringGet(); }
            set { this._XmlStringFill(value); }
        }
        private string _XmlStringGet()
        {
            float? all = this.All;
            if (all.HasValue)
            {   // Všechny 4 okraje jsou shodné:
                return Convertor.SingleToString(all.Value);
            }
            else
            {   // Převod 4 souřadnic typu float provedeme přes RectangleF, jen vyměníme zdrojové údaje:
                RectangleF helper = new RectangleF(this.Left, this.Top, this.Right, this.Bottom);
                return Convertor.RectangleFToString(helper);
            }

        }
        private void _XmlStringFill(string xmlString)
        {
            if (!xmlString.Contains(Convertor.INNER_SEPARARTOR))
            {   // Není oddělovač => všechny 4 okraje jsou shodné:
                this.All = (Single)Convertor.StringToSingle(xmlString);
            }
            else
            {   // Převod 4 souřadnic typu float provedeme přes RectangleF, na závěr vrátíme zdrojové údaje:
                RectangleF helper = (RectangleF)Convertor.StringToRectangleF(xmlString);
                this._Left = helper.X;
                this._Top = helper.Y;
                this._Right = helper.Width;
                this._Bottom = helper.Height;
            }
        }
    }
    #endregion
    #region PolygonF
    public class PolygonF
    {
        public PolygonF(params PointF[] points)
        {
            this.Points = points;
        }
        public PointF[] Points;
    }
    #endregion
    #region struct SheetF
    /// <summary>
    /// SheetF umožňuje uložení RectangleF nebo Region. Slouží k detekci, zda jistý bod leží uvnitř tohoto prostoru nebo ne.
    /// Prostor (díky použití Regionu) může mít libovolný tvar.
    /// </summary>
    public class SheetF
    {
        public SheetF(RectangleF area)
        {
            this.RectangleArea = area;
            this.ContentType = SheetContentType.Rectangle;
        }
        public SheetF(float x, float y, float width, float height)
        {
            this.RectangleArea = new RectangleF(x, y, width, height);
            this.ContentType = SheetContentType.Rectangle;
        }
        public SheetF(RectangleF area, RectangleF intersectArea)
        {
            this.RectangleArea = area;
            this.RectangleArea.Intersect(intersectArea);
            this.ContentType = SheetContentType.Rectangle;
        }
        public SheetF(PolygonF polygon)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(polygon.Points);
            path.CloseFigure();

            this.RegionArea = new Region(path);

            this.ContentType = SheetContentType.Region;
        }
        public SheetF(PolygonF polygon, RectangleF intersectArea)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(polygon.Points);
            path.CloseFigure();

            this.RegionArea = new Region(path);
            this.RegionArea.Intersect(intersectArea);

            this.ContentType = SheetContentType.Region;
        }
        public SheetF(GraphicsPath path)
        {
            this.RegionArea = new Region(path);
            this.ContentType = SheetContentType.Region;
        }
        public SheetF(GraphicsPath path, RectangleF intersectArea)
        {
            this.RegionArea = new Region(path);
            this.RegionArea.Intersect(intersectArea);
            this.ContentType = SheetContentType.Region;
        }
        private RectangleF RectangleArea;
        private Region RegionArea;
        private SheetContentType ContentType = SheetContentType.None;
        private enum SheetContentType { None, Rectangle, Region }


        public bool Contains(PointF point)
        {
            switch (this.ContentType)
            {
                case SheetContentType.None: return false;
                case SheetContentType.Rectangle: return this.RectangleArea.Contains(point);
                case SheetContentType.Region: return this.RegionArea.IsVisible(point);
            }
            return false;
        }

        #region Test rychlosti vytvoření a použití Regionu
        /// <summary>
        /// Test rychlosti vytvoření a použití Regionu
        /// </summary>
        public static void Test()
        {
            GraphicsPath path = new GraphicsPath();
            path.AddPolygon(new PointF[]
            {
                new PointF(20, 0),
                new PointF(80, 0),
                new PointF(100, 20),
                new PointF(100, 80),
                new PointF(80, 100),
                new PointF(20, 100),
                new PointF(0, 80),
                new PointF(0, 20),
                new PointF(20, 0)
            });
            path.CloseFigure();

            PointF[] points = new PointF[]
            {
                new PointF(0,0),
                new PointF(20,0),
                new PointF(20,20),
                new PointF(50,50),
                new PointF(80,80),
                new PointF(100,100)
            };

            bool[] expected = new bool[]
            {
                false,
                true,
                true,
                true,
                true,
                false
            };

            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            bool result;
            sw.Start();

            for (int i = 0; i < 100; i++)
            {
                Region region = new Region(path);
                result = region.IsVisible(points[0]);
            }
            long time1 = sw.ElapsedTicks;


            for (int i = 0; i < 1; i++)
            {
                Region region = new Region(path);
                for (int l = 0; l < 1000; l++)
                {
                    foreach (PointF pt in points)
                        result = region.IsVisible(pt);
                }
            }
            long time2 = sw.ElapsedTicks;

            sw.Stop();

            // vytvoření 1 region mikrosec = 13 mikrosec
            decimal timeCreate = (decimal)time1 / (decimal)System.Diagnostics.Stopwatch.Frequency * 10000m;

            // vyhledání 1 point mikrosec = 0.4 mikrosec
            decimal findTime = (decimal)(time2 - time1) / (decimal)System.Diagnostics.Stopwatch.Frequency * 1000000m / 6000m;
        }
        #endregion
        /// <summary>
        /// Ořízne plochu definovanou tímto objektem
        /// </summary>
        /// <param name="intersectWithArea"></param>
        internal void Intersect(RectangleF intersectWithArea)
        {
            switch (this.ContentType)
            {
                case SheetContentType.None:
                    break;
                case SheetContentType.Rectangle:
                    this.RectangleArea.Intersect(intersectWithArea);
                    break;
                case SheetContentType.Region:
                    this.RegionArea.Intersect(intersectWithArea);
                    break;
            }
        }
        /// <summary>
        /// Obsahuje true, pokud tento objekt vyjadřuje prázdný prostor
        /// </summary>
        public bool IsEmpty
        {
            get
            {
                switch (this.ContentType)
                {
                    case SheetContentType.None: return true;
                    case SheetContentType.Rectangle: return (this.RectangleArea.Width <= 0f || this.RectangleArea.Height <= 0f);
                    case SheetContentType.Region: return false;
                }
                return true;
            }
        }
    }
    #endregion
}
